package it.unica.informatica.enotes.manager;

import android.database.Cursor;
import java.util.ArrayList;
import java.util.Collections;
import it.unica.informatica.enotes.database.DatabaseAdapter;
import it.unica.informatica.enotes.notes.Note;

/**
 * This class provides the functions used by Activities to perform database queries
 *
 * @author Mirko Marras and Massimiliano Meles
 * @version 1.0
 */
public class NotesManager {

   /**
    * Get a note object that contains the data stored into the database for the note with the passed ID
    *
    * @param databaseAdapter The database used to retrieve the data
    * @param noteId          The ID of the note to retrieve
    * @return The note object that contains the data stored for the note with the passed ID
    */
   public static Note getNoteById(DatabaseAdapter databaseAdapter, Long noteId) {
        /* This note will store the content of the note with the requested ID */
      Note note = new Note();

        /* Perform the query to the database to retrieve the note associated with the requested ID */
      Cursor cursor = databaseAdapter.getNoteById(noteId);
      cursor.moveToFirst();

        /* Verify if the database contains a not with that ID */
      if (!cursor.isAfterLast()) {

            /* Retrieve the note data and store these values in the appropriate variables */
         Long id = cursor.getLong(cursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_NOTE_ID));
         String title = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_NOTE_TITLE));
         String lastChange = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_NOTE_LASTCHANGE));
         String extraData = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_NOTE_EXTRA_DATA));

            /* Set the note components based on the retrieved data */
         note.setId(id);
         note.setTitle(title);
         note.setContent(JsonManager.getNoteContent(extraData));
         note.setLastChange(lastChange);
         note.setAttachments(JsonManager.getNoteAttachments(extraData));
         note.setTag(NotesManager.getTagsByNoteId(databaseAdapter, noteId));
      }

        /* Close the cursor used to perform the query */
      cursor.close();

      return note;
   }

   /**
    * Delete from the database the note with the passed ID
    *
    * @param databaseAdapter The database used to manage the data
    * @param noteId          The ID of the note to delete
    * @return True if the note is deleted, false otherwise
    */
   public static boolean deleteNoteById(DatabaseAdapter databaseAdapter, Long noteId) {
      return databaseAdapter.deleteNote(noteId);
   }

   /**
    * Create new tag-note associations for the note with the passed ID and remove old associations
    *
    * @param databaseAdapter The database used to manage the data
    * @param noteId          The ID of the note to modify
    * @param tags            The list of the new tags to link with the note with the passed ID
    */
   private static void refreshTagsForNote(DatabaseAdapter databaseAdapter, Long noteId, ArrayList<String> tags) {
      Cursor oldCursor = databaseAdapter.getTagsByNote(noteId);
      oldCursor.moveToFirst();

        /* Delete all the current associations tag-note from the database, only for the note with the requested ID */
      databaseAdapter.deleteAllLinks(noteId);

        /* Check if the tag is used in another note */
      while (!oldCursor.isAfterLast()) {
         long currentTagId = oldCursor.getLong(oldCursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_TAG_ID));
         if (!databaseAdapter.isExistentTag(currentTagId)) {
            databaseAdapter.deleteTag(currentTagId);
         }
         oldCursor.moveToNext();
      }

        /* For each new tag, it is inserted an associations from this tag to the note */
      for (String tag : tags) {
            /* Perform a query to control if the current tag already exist in the database */
         Cursor newCursor = databaseAdapter.getTagIdByName(tag);
         newCursor.moveToFirst();

         Long tagId;
         if (newCursor.isAfterLast()) { /* If the current tag doesn't exist, it is inserted in the database */
            tagId = databaseAdapter.createTag(tag);
         } else { /* If the current tag already exist, take its ID */
            tagId = newCursor.getLong(newCursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_TAG_ID));
         }

            /* Close the cursor used to perform the query */
         newCursor.close();

            /* Perform a query to create an association from the tag to the note */
         databaseAdapter.createLink(noteId, tagId);
      }
   }

   /**
    * Get the list of all the tags names associated to the note with the passed ID
    *
    * @param databaseAdapter The database used to manage the data
    * @param noteId          The ID of the note to manage
    * @return The list of all the tags names associated to the note with the passed ID
    */
   private static ArrayList<String> getTagsByNoteId(DatabaseAdapter databaseAdapter, Long noteId) {
        /* This ArrayList will store the tags of the note with the requested ID */
      ArrayList<String> tags = new ArrayList<String>();

        /* Perform the query to the database to retrieve all tags associated with the requested note */
      Cursor cursor = databaseAdapter.getTagsByNote(noteId);
      cursor.moveToFirst();

        /* Add each retrieved tag to the result */
      while (!cursor.isAfterLast()) {
         tags.add(cursor.getString(cursor.getColumnIndexOrThrow(DatabaseAdapter.KEY_TAG_NAME)));
         cursor.moveToNext();
      }

        /* Close the cursor used to perform the query */
      cursor.close();

      return tags;
   }

   /**
    * Create a new note with the data contained in the note object passed as parameter
    *
    * @param databaseAdapter The database used to manage the data
    * @param note            The note object that contains the data of the new note
    * @return The ID of the new note inserted into the database
    */
   public static Long createNote(DatabaseAdapter databaseAdapter, Note note) {
        /* Insert the new note in the database and take its ID */
      Long noteId = databaseAdapter.createNote(note.getTitle(), JsonManager.toJSON(note));
        /* Create the associations from the note to its tags */
      NotesManager.refreshTagsForNote(databaseAdapter, noteId, note.getTags());

      return noteId;
   }

   /**
    * Update an existent note with the data contained in the note object passed as parameter
    *
    * @param databaseAdapter The database used to manage the data
    * @param note            The note object that contains the new data for the note
    * @return True if the note is updated, false otherwise
    */
   public static boolean updateNote(DatabaseAdapter databaseAdapter, Note note) {
        /* Create the associations from the note to its tags */
      NotesManager.refreshTagsForNote(databaseAdapter, note.getId(), note.getTags());

      return databaseAdapter.updateNote(note.getId(), note.getTitle(), JsonManager.toJSON(note));
   }

   /**
    * Load all notes from the Database
    *
    * @param databaseAdapter The database used to manage the data
    * @param notes           The notes list with the notes showed on MainScreenActivity
    */
   public static void fetchAllNotes(DatabaseAdapter databaseAdapter, ArrayList<Note> notes) {

      notes.clear();
      //Retrieve notes for Database
      Cursor cursor = databaseAdapter.fetchAllNotes();

      cursor.moveToFirst();
      //Scan all rows in the result from the query
      while (!cursor.isAfterLast()) {
         notes.add(new Note(cursor.getLong(0), cursor.getString(1), cursor.getString(2)));
         cursor.moveToNext();
      }
      cursor.close();
      Collections.sort(notes);
   }

   /**
    * Load all notes from the Database
    *
    * @param databaseAdapter The database used to manage the data
    * @param notes           The notes list with the notes showed on MainScreenActivity
    */
   public static void fetchNoteByTag(DatabaseAdapter databaseAdapter, ArrayList<Note> notes, String query) {
      notes.clear();
      Cursor c = databaseAdapter.fetchNoteByTag(query);
      c.moveToFirst();
      while (!c.isAfterLast()) {
         notes.add(new Note(c.getLong(0), c.getString(1), c.getString(2)));
         c.moveToNext();
      }
      c.close();
      Collections.sort(notes);
   }

}
